/*-------------------------------------------------------------------------
 * Filename:      main.c
 * Version:       $Id: main.c,v 1.23 2002/01/07 14:48:25 seletz Exp $
 * Copyright:     Copyright (C) 1999, Jan-Derk Bakker
 * Author:        Jan-Derk Bakker <J.D.Bakker@its.tudelft.nl>
 * Description:   Main file for the trivially simple bootloader for the 
 *                LART boards
 * Created at:    Mon Aug 23 20:00:00 1999
 * Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
 * Modified at:   Sat Mar 25 14:31:16 2000
 *-----------------------------------------------------------------------*/
/*
 * main.c: main file for the blob bootloader
 *
 * Copyright (C) 1999 2000 2001
 *   Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl) and
 *   Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ident "$Id: main.c,v 1.23 2002/01/07 14:48:25 seletz Exp $"

#ifdef HAVE_CONFIG_H
# include <blob/config.h>
#endif

#include <blob/arch.h>
#include <blob/command.h>
#include <blob/errno.h>
#include <blob/error.h>
#include <blob/flash.h>
#include <blob/init.h>
#include <blob/led.h>
#include <blob/main.h>
#include <blob/md5.h>
#include <blob/md5support.h>
#include <blob/memory.h>
#include <blob/param_block.h>
#include <blob/serial.h>
#include <blob/time.h>
#include <blob/util.h>
#include <blob/uucodec.h>
#include <blob/xmodem.h>
#include <blob/time.h>




static int do_reload(char *what);
static void PrintSerialSpeed(serial_baud_t speed);
static void print_elf_sections(void);




blob_status_t blob_status;
static char *version_str = PACKAGE " version " VERSION " for " BOARD_NAME "\n";

int main(void)
{
	int numRead = 0;
	int done=0;
	char commandline[MAX_COMMANDLINE_LENGTH];
	int i;
	int retval = 0;
#ifdef PARAM_START
	u32 conf;
#endif

	/* call subsystems */
	init_subsystems();

	/* initialise status */
	blob_status.paramType = fromFlash;
	blob_status.kernelType = fromFlash;
	blob_status.ramdiskType = fromFlash;
	blob_status.downloadSpeed = baud_115200;
	blob_status.terminalSpeed = baud_115200;
	blob_status.load_ramdisk = 1;
	blob_status.cmdline[0] = '\0';
	blob_status.boot_delay = 10;

	/* call serial_init() because the default 9k6 speed might not
           be what the user requested */
#if defined(H3600) || defined(SHANNON) || defined(IDR) || defined(BADGE4) || defined(JORNADA720) || defined(YF255)
	blob_status.terminalSpeed = baud_115200; /* DEBUG */
#endif
#if defined(PT_SYSTEM3)
	blob_status.terminalSpeed = baud_38400;
#endif
	serial_init(blob_status.terminalSpeed);

	/* parse the core tag, for critical things like terminal speed */
#ifdef PARAM_START
	//parse_ptag((void *) PARAM_START, &conf);
#endif

	/* Print the required GPL string */
	SerialOutputString("\nConsider yourself LARTed!\n\n");
	SerialOutputString(version_str);
	SerialOutputString("Copyright (C) 1999 2000 2001 "
			   "Jan-Derk Bakker and Erik Mouw\n");
	SerialOutputString(PACKAGE " comes with ABSOLUTELY NO WARRANTY; "
			   "read the GNU GPL for details.\n");
	SerialOutputString("This is free software, and you are welcome "
			   "to redistribute it\n");
	SerialOutputString("under certain conditions; "
			   "read the GNU GPL for details.\n");

	/* get the amount of memory */
	get_memory_map();

	print_elf_sections();
#ifdef BLOB_DEBUG
	{
		register u32 stackptr;
		SerialOutputString("Current stack pointer: 0x");
		asm("mov %0, sp": "=r" (stackptr));
		SerialOutputHex(stackptr);
		serial_write('\n');
	}
#endif

	/* Parse all the tags in the paramater block */
#ifdef PARAM_START
	//parse_ptags((void *) PARAM_START, &conf);
#endif

	/* Load kernel and ramdisk from flash to RAM */
	do_reload("blob");
	do_reload("kernel");
	/*
	if(blob_status.load_ramdisk)
		do_reload("ramdisk");
	*/

	/* wait 10 seconds before starting autoboot */
	SerialOutputString("Autoboot in progress, press any key to stop ");
	for(i = 0; i < blob_status.boot_delay; i++) {
		serial_write('.');

 		retval = SerialInputBlock(commandline, 1, 1); 

		if(retval > 0)
			break;
	}


	/* no key was pressed, so proceed booting the kernel */
	if(retval == 0) {
		commandline[0] = '\0';
		parse_command("boot");
	}


	SerialOutputString("\nAutoboot aborted\n");
	SerialOutputString("Type \"help\" to get a list of commands\n");

	DisplayPrompt(NULL);
	numRead = 0;
	done = 0;

	/* the command loop. endless, of course */
	for(;;) {
		char c;
		char ready;

		/* wait 10 minutes for a command */
		//numRead = GetCommand(commandline, MAX_COMMANDLINE_LENGTH, 600);

#if (defined(ETHERNET))
		eth_rx();
#endif

		ready = serial_poll();
		if ( ready == 1 )  {

			c = serial_read();

			if(c < 0) {
				serial_write('\n');
				continue;
			}

			if((c == '\r') || (c == '\n')) {
				commandline[numRead++] = '\0';
				done = 1;

				serial_write('\n');
			} else if(c == '\b') { 
				if(numRead > 0) {
					numRead--;
					SerialOutputString("\b \b");
				}
			} else {
				commandline[numRead++] = c;

				serial_write(c);
			}
		}


		if(done) {
			if((retval = parse_command(commandline)) < 0 )
				printerror(retval, NULL);

			/* reset count */
			done = 0;
			numRead = 0;

			DisplayPrompt(NULL);
		}
	}

	return 0;
} /* main */




static int set_download_parameters(char *name, u32 *startAddress, 
				    int *bufLen, int **numRead,
				   u32 **digest)
{
	if(strncmp(name, "blob", 5) == 0) {
		/* download blob */
		*startAddress = BLOB_RAM_BASE;
		*bufLen = BLOB_FLASH_LEN;
		*numRead = &blob_status.blobSize;
		*digest = blob_status.blob_md5_digest;
		blob_status.blobType = fromDownload;
#ifdef PARAM_START
	} else if(strncmp(name, "param", 6) == 0) {
		/* download param */
		*startAddress = PARAM_RAM_BASE;
		*bufLen = PARAM_FLASH_LEN;
		*numRead = &blob_status.paramSize;
		*digest = blob_status.param_md5_digest;
		blob_status.paramType = fromDownload;
#endif
	} else if(strncmp(name, "kernel", 7) == 0) {
		/* download kernel */
		*startAddress = KERNEL_RAM_BASE;
		*bufLen = KERNEL_FLASH_LEN;
		*numRead = &blob_status.kernelSize;
		*digest = blob_status.kernel_md5_digest;
		blob_status.kernelType = fromDownload;
	} else if(strncmp(name, "ramdisk", 8) == 0) {
		/* download ramdisk */
		*startAddress = RAMDISK_RAM_BASE;
		*bufLen = RAMDISK_FLASH_LEN;
		*numRead = &blob_status.ramdiskSize;
		*digest = blob_status.ramdisk_md5_digest;
		blob_status.ramdiskType = fromDownload;
	} else {
		printerror(-EINVAL, name);
		return -EINVAL;
	}

	return 0;
}




static void do_md5sum(u32 *addr, size_t len, u32 *digest)
{
#ifdef CONFIG_MD5_SUPPORT
	md5_buffer((const char *)addr, len, digest);
#endif
}




static int Download(int argc, char *argv[])
{
	u32 startAddress;
	int bufLen;
	int *numRead;
	int retval;
	u32 *digest;

	if(argc < 2)
		return -ENOPARAMS;

	retval = set_download_parameters(argv[1], &startAddress,
					 &bufLen, &numRead, &digest);

	if (blob_status.terminalSpeed != blob_status.downloadSpeed) {
		SerialOutputString("Switching to ");
		PrintSerialSpeed(blob_status.downloadSpeed);
		SerialOutputString(" baud\n");

		SerialOutputString("You have 60 seconds to switch your terminal emulator to the same speed and\n");
		SerialOutputString("start downloading. After that " PACKAGE " will switch back to ");
		PrintSerialSpeed(blob_status.terminalSpeed);
		SerialOutputString(" baud.\n");
	
		serial_init(blob_status.downloadSpeed);
	} else {
		SerialOutputString("You have 60 seconds to start downloading.\n");
	}

	*numRead = UUDecode((char *)startAddress, bufLen);
	
	if (blob_status.terminalSpeed != blob_status.downloadSpeed) {
		SerialOutputString("\n(Please switch your terminal emulator back to ");
		PrintSerialSpeed(blob_status.terminalSpeed);
		SerialOutputString(" baud)\n");
	}
	
	if(*numRead < 0) {
		/* something went wrong */
		retval = *numRead;
		
		/* reload the correct memory */
		do_reload(argv[1]);

		serial_init(blob_status.terminalSpeed);
		return retval;
	}

	do_md5sum((u32 *)startAddress, *numRead, digest);

	SerialOutputString("Received ");
	SerialOutputDec(*numRead);
	SerialOutputString(" (0x");
	SerialOutputHex(*numRead);
	SerialOutputString(") bytes");
#ifdef BLOB_DEBUG
	SerialOutputString(" at 0x");
	SerialOutputHex((u32) startAddress);
#ifdef CONFIG_MD5_SUPPORT
	SerialOutputString(", MD5: ");
	print_md5_digest(digest);
#endif
#endif
	serial_write('\n');


	serial_init(blob_status.terminalSpeed);

	return 0;
}

static char downloadhelp[] = "download {blob|param|kernel|ramdisk}\n"
"Download <argument> image to RAM using uuencode\n";

__commandlist(Download, "download", downloadhelp);

#if defined(LUBBOCK) || defined(YF255)
static int Go(int argc, char *argv[]){
	unsigned long go=0, unused=0;
	
	if(argc < 2)
		return -ENOPARAMS;

	strtou32(argv[1], (unsigned long*)(&go));
	__asm("
	@ drain pending loads and stores
	mcr 	p15, 0, r0, c7, c10, 4
	mrc  	p15,0,%1,c2,c0,0
	mov  	%1,%1
	sub  	pc,pc,#4

	mov    	%1, #0x78			@turn everything off
      mcr    	p15, 0, %1, c1, c0, 0		@(caches off, MMU off, etc.)
	mrc  	p15,0,%1,c2,c0,0
	mov  	%1,%1
	sub  	pc,pc,#4			

	mov 	pc, %0
	"
	:
	:"r"(go), "r"(unused)
	);
	return 0;
}

static char gohelp[] = "go address\n"
"go and execute code at <address>, address in hex \n";

__commandlist(Go, "go", gohelp);

#endif

static int xdownload(int argc, char *argv[])
{
	u32 startAddress;
	int bufLen;
	int *numRead;
	int retval;
	u32 *digest;

	if(argc < 2)
		return -ENOPARAMS;

	retval = set_download_parameters(argv[1], &startAddress,
					 &bufLen, &numRead, &digest);

	if (blob_status.terminalSpeed != blob_status.downloadSpeed) {
		SerialOutputString("Switching to ");
		PrintSerialSpeed(blob_status.downloadSpeed);
		SerialOutputString(" baud\n");

		SerialOutputString("Switch your terminal emulator to the same speed and\n");
		SerialOutputString("start sending using the XMODEM protocl (repeated ^X) to quit.\n");
		SerialOutputString(PACKAGE " will switch back to ");
		PrintSerialSpeed(blob_status.terminalSpeed);
		SerialOutputString(" baud after XMODEM succeeds or times out.\n");
	
		serial_init(blob_status.downloadSpeed);
	} else {
		SerialOutputString("Start sending using the XMODEM protocol (repeated ^X to quit).\n\n");
	}

	*numRead = XModemReceive((char *) startAddress, bufLen);

	SerialOutputString("\n\n\n");
	
	if (blob_status.terminalSpeed != blob_status.downloadSpeed) {
		SerialOutputString("\n(Please switch your terminal emulator back to ");
		PrintSerialSpeed(blob_status.terminalSpeed);
		SerialOutputString(" baud)\n");
	}
	
	if(*numRead < 0) {
		/* something went wrong */
		retval = *numRead;
		
		/* reload the correct memory */
		do_reload(argv[1]);

		serial_init(blob_status.terminalSpeed);
		return retval;
	}

	do_md5sum((u32 *)startAddress, *numRead, digest);

	SerialOutputString("Received ");
	SerialOutputDec(*numRead);
	SerialOutputString(" (0x");
	SerialOutputHex(*numRead);
	SerialOutputString(") bytes");
#ifdef BLOB_DEBUG
	SerialOutputString(" at 0x");
	SerialOutputHex((u32) startAddress);
#ifdef CONFIG_MD5_SUPPORT
	SerialOutputString(", MD5: ");
	print_md5_digest(digest);
#endif
#endif
	serial_write('\n');


	serial_init(blob_status.terminalSpeed);

	return 0;
}

static char xdownloadhelp[] = "xdownload {blob|param|kernel|ramdisk}\n"
"Download <argument> image to RAM using xmodem\n";

__commandlist(xdownload, "xdownload", xdownloadhelp);


static int Flash(int argc, char *argv[])
{
	u32 *src;
	u32 *dst;
	u32 numBytes = 0;
	u32 maxSize = 0;
	u32 nwords;
	block_source_t type = fromFlash;
	

	if(argc < 2)
		return -ENOPARAMS;

	if(strncmp(argv[1], "blob", 5) == 0) {
		src = (u32 *)BLOB_RAM_BASE;
		dst = (u32 *)BLOB_FLASH_BASE;
		maxSize = BLOB_FLASH_LEN;
		numBytes = blob_status.blobSize;
		type = blob_status.blobType;
#ifdef PARAM_START
	} else if(strncmp(argv[1], "param", 6) == 0) {
		src = (u32 *)PARAM_RAM_BASE;
		dst = (u32 *)PARAM_FLASH_BASE;
		maxSize = PARAM_FLASH_LEN;
		numBytes = blob_status.paramSize;
		type = blob_status.paramType;
#endif
	} else if(strncmp(argv[1], "kernel", 7) == 0) {
		src = (u32 *)KERNEL_RAM_BASE;
		dst = (u32 *)KERNEL_FLASH_BASE;
		numBytes = blob_status.kernelSize;
		maxSize = KERNEL_FLASH_LEN;
		type = blob_status.kernelType;
	} else if(strncmp(argv[1], "ramdisk", 8) == 0) {
		src = (u32 *)RAMDISK_RAM_BASE;
		dst = (u32 *)RAMDISK_FLASH_BASE;
		numBytes = blob_status.ramdiskSize;
		maxSize = RAMDISK_FLASH_LEN;
		type = blob_status.ramdiskType;
	} else {
		printerror(EINVAL, argv[1]);
		return 0;
	}

	if(type == fromFlash) {
		printerrprefix();
		SerialOutputString(argv[1]);
		SerialOutputString(" not downloaded\n");
		return -EINVAL;
	}

	if(numBytes > maxSize) {
		printerrprefix();
		SerialOutputString("image too large for flash: 0x");
		SerialOutputHex(numBytes);
		SerialOutputString(" > 0x");
		SerialOutputHex(maxSize);
		serial_write('\n');

		return -ETOOLONG;
	}

	nwords = (numBytes + sizeof(u32) - 1) / sizeof(u32);

	SerialOutputString("Saving ");
	SerialOutputString(argv[1]);
	SerialOutputString(" to flash\n");


	flash_write_region(dst, src, nwords);

	return 0;
}

static char flashhelp[] = "flash {blob|param|kernel|ramdisk}\n"
"Write <argument> image to flash\n";

__commandlist(Flash, "flash", flashhelp);




static int SetDownloadSpeed(int argc, char *argv[])
{
	if(argc < 2)
		return -ENOPARAMS;

	if(strncmp(argv[1], "1200", 5) == 0) {
		blob_status.downloadSpeed = baud_1200;
	} else if(strncmp(argv[1], "1k2", 4) == 0) {
		blob_status.downloadSpeed = baud_1200;
	} else if(strncmp(argv[1], "9600", 5) == 0) {
		blob_status.downloadSpeed = baud_9600;
	} else if(strncmp(argv[1], "9k6", 4) == 0) {
		blob_status.downloadSpeed = baud_9600;
	} else if(strncmp(argv[1], "19200", 6) == 0) {
		blob_status.downloadSpeed = baud_19200;
	} else if(strncmp(argv[1], "19k2", 5) == 0) {
		blob_status.downloadSpeed = baud_19200;
	} else if(strncmp(argv[1], "38400", 7) == 0) {
		blob_status.downloadSpeed = baud_38400;
	} else if(strncmp(argv[1], "38k4", 5) == 0) {
		blob_status.downloadSpeed = baud_38400;
	} else if(strncmp(argv[1], "57600", 6) == 0) {
		blob_status.downloadSpeed = baud_57600;
	} else if(strncmp(argv[1], "57k6", 5) == 0) {
		blob_status.downloadSpeed = baud_57600;
	} else if(strncmp(argv[1], "115200", 7) == 0) {
		blob_status.downloadSpeed = baud_115200;
	} else if(strncmp(argv[1], "115k2", 6) == 0) {
		blob_status.downloadSpeed = baud_115200;
	} else if(strncmp(argv[1], "230400", 7) == 0) {
		blob_status.downloadSpeed = baud_230400;
	} else if(strncmp(argv[1], "230k4", 6) == 0) {
		blob_status.downloadSpeed = baud_230400;
	} else {
		return -EINVAL;
	}

	SerialOutputString("Download speed set to ");
	PrintSerialSpeed(blob_status.downloadSpeed);
	SerialOutputString(" baud\n");

	return 0;
}

static char speedhelp[] = "speed [baudrate]\n"
"Set download speed. Valid baudrates are:\n"
"1200, 9600, 19200, 38400, 57600, 115200, 230400,\n"
" 1k2,  9k6,  19k2,  38k4,  57k6,  115k2,  230k4\n";

__commandlist(SetDownloadSpeed, "speed", speedhelp);




static int PrintStatus(int argc, char *argv[])
{
	SerialOutputString(version_str);

	SerialOutputString("Download speed      : ");
	PrintSerialSpeed(blob_status.downloadSpeed);
	SerialOutputString(" baud\n");

	SerialOutputString("Terminal speed      : ");
	PrintSerialSpeed(blob_status.terminalSpeed);
	SerialOutputString(" baud\n");

	SerialOutputString("blob    (0x");
	SerialOutputHex(BLOB_FLASH_BASE);
	SerialOutputString("): ");
	if(blob_status.blobType == fromFlash) {
		SerialOutputString("from flash\n");
	} else {
		SerialOutputString("downloaded at 0x");
		SerialOutputHex(BLOB_RAM_BASE);
		SerialOutputString(", ");
		SerialOutputDec(blob_status.blobSize);
		SerialOutputString(" bytes\n");
#ifdef CONFIG_MD5_SUPPORT
		SerialOutputString("                      MD5: ");
		print_md5_digest(blob_status.blob_md5_digest);
		serial_write('\n');
#endif
	}

#ifdef PARAM_START
	SerialOutputString("param  (0x");
	SerialOutputHex(PARAM_FLASH_BASE);
	SerialOutputString("): ");
	if(blob_status.paramType == fromFlash) {
		SerialOutputString("from flash\n");
	} else {
		SerialOutputString("downloaded at 0x");
		SerialOutputHex(PARAM_RAM_BASE);
		SerialOutputString(", ");
		SerialOutputDec(blob_status.paramSize);
		SerialOutputString(" bytes\n");
#ifdef CONFIG_MD5_SUPPORT
		SerialOutputString("                      MD5: ");
		print_md5_digest(blob_status.param_md5_digest);
		serial_write('\n');
#endif
	}
#else
	SerialOutputString("param               : Not available\n");
#endif

	SerialOutputString("kernel  (0x");
	SerialOutputHex(KERNEL_FLASH_BASE);
	SerialOutputString("): ");
	if(blob_status.kernelType == fromFlash) {
		SerialOutputString("from flash\n");
	} else {
		SerialOutputString("downloaded at 0x");
		SerialOutputHex(KERNEL_RAM_BASE);
		SerialOutputString(", ");
		SerialOutputDec(blob_status.kernelSize);
		SerialOutputString(" bytes\n");
#ifdef CONFIG_MD5_SUPPORT
		SerialOutputString("                      MD5: ");
		print_md5_digest(blob_status.kernel_md5_digest);
		serial_write('\n');
#endif
	}

	SerialOutputString("ramdisk (0x");
	SerialOutputHex(RAMDISK_FLASH_BASE);
	SerialOutputString("): ");
	if(blob_status.ramdiskType == fromFlash) {
		SerialOutputString("from flash\n");
	} else {
		SerialOutputString("downloaded at 0x");
		SerialOutputHex(RAMDISK_RAM_BASE);
		SerialOutputString(", ");
		SerialOutputDec(blob_status.ramdiskSize);
		SerialOutputString(" bytes\n");
#ifdef CONFIG_MD5_SUPPORT
		SerialOutputString("                      MD5: ");
		print_md5_digest(blob_status.ramdisk_md5_digest);
		serial_write('\n');
#endif
	}
	
	return 0;
}

static char statushelp[] = "status\n"
"Display current status\n";

__commandlist(PrintStatus, "status", statushelp);




static int do_reload(char *what)
{
	u32 *dst = 0;
	u32 *src = 0;
	int numWords;

	if(strncmp(what, "blob", 5) == 0) {
		dst = (u32 *)BLOB_RAM_BASE;
		src = (u32 *)BLOB_FLASH_BASE;
		numWords = BLOB_FLASH_LEN / 4;
		blob_status.blobSize = 0;
		blob_status.blobType = fromFlash;
		SerialOutputString("Loading blob from flash ");
#ifdef PARAM_START
	} else if(strncmp(what, "param", 6) == 0) {
		dst = (u32 *)PARAM_RAM_BASE;
		src = (u32 *)PARAM_FLASH_BASE;
		numWords = PARAM_FLASH_LEN / 4;
		blob_status.paramSize = 0;
		blob_status.paramType = fromFlash;
		SerialOutputString("Loading paramater block from flash ");
#endif
	} else if(strncmp(what, "kernel", 7) == 0) {
		dst = (u32 *)KERNEL_RAM_BASE;
		src = (u32 *)KERNEL_FLASH_BASE;
		numWords = KERNEL_FLASH_LEN / 4;
		blob_status.kernelSize = 0;
		blob_status.kernelType = fromFlash;
		SerialOutputString("Loading kernel from flash ");
	} else if(strncmp(what, "ramdisk", 8) == 0) {
		dst = (u32 *)RAMDISK_RAM_BASE;
		src = (u32 *)RAMDISK_FLASH_BASE;
		numWords = RAMDISK_FLASH_LEN / 4;
		blob_status.ramdiskSize = 0;
		blob_status.ramdiskType = fromFlash;
		SerialOutputString("Loading ramdisk from flash ");
	} else {
		printerror(EINVAL, what);
		return 0;
	}

	MyMemCpy(dst, src, numWords);
	SerialOutputString(" done\n");

	return 0;
}




static int Reload(int argc, char *argv[])
{
	if(argc < 2)
		return -ENOPARAMS;

	return do_reload(argv[1]);
}

static char reloadhelp[] = "reload {blob|param|kernel|ramdisk}\n"
"Reload <argument> from flash to RAM\n";

__commandlist(Reload, "reload", reloadhelp);




static void PrintSerialSpeed(serial_baud_t speed)
{
	switch(speed) {
	case baud_1200:
		SerialOutputDec(1200);
		break;

	case baud_9600:
		SerialOutputDec(9600);
		break;

	case baud_19200:
		SerialOutputDec(19200);
		break;

	case baud_38400:
		SerialOutputDec(38400);
		break;

	case baud_57600:
		SerialOutputDec(57600);
		break;

	case baud_115200:
		SerialOutputDec(115200);
		break;

	case baud_230400:
		SerialOutputDec(230400);
		break;

	default:
		SerialOutputString("(unknown)");
		break;
	}
}




static void print_elf_sections(void)
{
#ifdef BLOB_DEBUG
	extern u32 __text_start, __text_end;
	extern u32 __rodata_start, __rodata_end;
	extern u32 __data_start, __data_end;
	extern u32 __got_start, __got_end;
	extern u32 __commandlist_start, __commandlist_end;
	extern u32 __initlist_start, __initlist_end;
	extern u32 __exitlist_start, __exitlist_end;
	extern u32 __ptagtable_begin, __ptagtable_end;
	extern u32 __bss_start, __bss_end;
	extern u32 __stack_start, __stack_end;

	SerialOutputString("ELF sections layout:\n");

#define PRINT_ELF_SECTION(from, to, name) \
	SerialOutputString("  0x"); \
	SerialOutputHex((u32)&(from)); \
	SerialOutputString(" - 0x"); \
	SerialOutputHex((u32)&(to)); \
	SerialOutputString(" " name "\n");


	PRINT_ELF_SECTION(__text_start, __text_end, ".text");
	PRINT_ELF_SECTION(__rodata_start, __rodata_end, ".rodata");
	PRINT_ELF_SECTION(__data_start, __data_end, ".data");
	PRINT_ELF_SECTION(__got_start, __got_end, ".got");
	PRINT_ELF_SECTION(__commandlist_start, __commandlist_end, ".commandlist");
	PRINT_ELF_SECTION(__initlist_start, __initlist_end, ".initlist");
	PRINT_ELF_SECTION(__exitlist_start, __exitlist_end, ".exitlist");
	PRINT_ELF_SECTION(__ptagtable_begin, __ptagtable_end, ".ptaglist");
	PRINT_ELF_SECTION(__bss_start, __bss_end, ".bss");
	PRINT_ELF_SECTION(__stack_start, __stack_end, ".stack (in .bss)");
#endif
}
